﻿using Client.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;

[Serializable]
public class SerializeData
{
    public GameObject go;
    public int maskIdx;
}

[DisallowMultipleComponent]
public class SerializeRoot : SerializeNode
{
    [HideInInspector]
    public bool needRemove = true;
    public string ClassName { get; set; }

    [HideInInspector]
    public List<SerializeData> childs = new List<SerializeData>();
    Dictionary<string, UIInfo> infoDic = new Dictionary<string, UIInfo>();

    public string SerProperty
    {
        get { return needRemove ? "Serialize" : "Deserialize"; }
    }

    public void OperateNode()
    {
        foreach (var serialize in childs)
        {
            if (needRemove)
            {
                var node = serialize.go.GetComponent<SerializeNode>();
                if (IsSerializeNode(node)) { DestroyImmediate(node); }
            }
            else
            {
                var node = serialize.go.AddMissingComponent<SerializeNode>();
                node.maskIdx = serialize.maskIdx;
            }
        }
        needRemove = !needRemove;
    }

    public void CheckData()
    {
        for (int i = keyObjDatas.Count - 1; i >= 0; i--)
        {
            Component com = keyObjDatas[i].com;
            if (com && com is SerializeRoot)
            {
                keyObjDatas[i].name = GetHashCodeName(com);
            }
            else { keyObjDatas.RemoveAt(i); }
        }

        childs.Reverse();
        for (int i = childs.Count - 1; i >= 0; i--)
        {
            SerializeData serialize = childs[i];
            if (serialize.go && IsRootBelow(serialize.go))
            {
                AddKeyObjData(serialize.go, serialize.maskIdx);
            }
            else { childs.RemoveAt(i); }
        }
        childs.Reverse(); names = AddKeyObjData(gameObject, maskIdx);
    }

    public override void FlushData()
    {
        childs.Clear(); keyObjDatas.Clear();
        var nodes = new List<SerializeNode>();

        FindAllChild(ref nodes, transform);
        foreach (var serialize in nodes)
        {
            Add(serialize);
            AddNode(serialize);
        }
        names = AddKeyObjData(gameObject, maskIdx);
    }

    protected void FindAllChild(ref List<SerializeNode> list, Transform tf)
    {
        for (int i = 0; i < tf.childCount; i++)
        {
            Transform child = tf.GetChild(i);
            var root = child.GetComponent<SerializeRoot>();
            var node = child.GetComponent<SerializeNode>();

            if (root != null)
            {
                Add(root);
                continue;
            }
            else
            {
                if (node != null)
                {
                    node.FlushData();
                    list.Add(node);
                }
                FindAllChild(ref list, child);
            }
        }
    }

    protected bool IsRootBelow(GameObject go)
    {
        Transform tf = go.transform;
        for (; ; )
        {
            if (tf == null) { break; }
            else
            {
                var root = tf.GetComponent<SerializeRoot>();
                if (root != null) { break; }
                else { tf = tf.parent; }
            }
        }
        return tf == transform;
    }

    protected bool IsExistNode(SerializeNode node)
    {
        foreach (var serialize in childs)
        {
            if (serialize.go == node.gameObject)
            {
                return true;
            }
        }
        return false;
    }

    protected void AddNode(SerializeNode node)
    {
        if (!IsExistNode(node))
        {
            var serialize = new SerializeData()
            {
                go = node.gameObject,
                maskIdx = node.maskIdx,
            };
            childs.Add(serialize);
        }
    }

    public List<KeyObjectData> GetKeyObjectDatas()
    {
        var keyObjects = new List<KeyObjectData>();
        foreach (var serialize in keyObjDatas)
        {
            if (serialize.com is SerializeRoot)
            {
                keyObjects.Add(serialize);
            }
        }
        return keyObjects;
    }

    public void SaveScript(string classPath, string template, bool script, bool findGenerate)
    {
        StreamReader streamReader = new StreamReader(template);
        string content = streamReader.ReadToEnd(); streamReader.Close();

        if (findGenerate)
        {
            GenerateScript(script, ref content);
        }
        else { GenerateString(script, ref content); }

        if (IsValidName(ClassName))
        {
            StreamWriter writer = new StreamWriter(classPath, false, new UTF8Encoding());
            content = Regex.Replace(content, "@ClassName", ClassName);
            writer.Write(content); writer.Close();
        }
        else { Debug.LogWarning($"{ClassName} is not invalid keyName", gameObject); }
    }

    protected void GenerateScript(bool script, ref string content)
    {
        BuildChildInfo(keyObjDatas, script);
        var fields = new StringBuilder();
        var body = new StringBuilder();

        for (int i = 0; i < infoDic.Count; i++)
        {
            UIInfo info = infoDic.GetValueByIdx(i, null);
            if (info.IsSelected)
            {
                body.Append(info.Body);
                fields.Append(info.Field);
            }
        }
        content = Regex.Replace(content, "@body", body.ToString());
        content = Regex.Replace(content, "@fields", fields.ToString());
    }

    protected void GenerateString(bool script, ref string content)
    {
        if (script)
        {
            content = Regex.Replace(content, "@fields", GetFieldString());
            content = Regex.Replace(content, "@body", GetBodyString());
        }
        else
        {
            content = Regex.Replace(content, "@fields", GetLuaFieldString());
            content = Regex.Replace(content, "@body", GetLuaBodyString());
        }
    }

    protected void BuildChildInfo(List<KeyObjectData> keyObjects, bool script)
    {
        keyObjects.ForEach(item =>
        {
            if (IsSerializeNode(item.com))
            {
                var node = (SerializeNode)item.com;
                BuildChildInfo(node.keyObjDatas, script);
            }
            else
            {
                string path = item.com.BuildTfPath(transform);
                AddUIInfo(item.name, item.com.GetType(), path, script);
            }
        });
    }

    protected void AddUIInfo(string name, Type type, string path, bool script)
    {
        var info = new UIInfo(name);
        infoDic[info.Name] = info;

        if (type == typeof(SerializeRoot))
        {
            type = typeof(GameObject);
        }

        if (script)
        {
            info.InitCSInfo(type, path);
        }
        else { info.InitLuaInfo(type, path); }
    }
}